package ua.yandex.collections;

/**
 *
 * @author Владислав
 */
public class MyLinkedList implements MyList {

    private Node head;
    private int nodeCount;

    public void addFirst(Object e) {
        add(0, e);
    } // добавляет элемент в начало

    public void addLast(Object e) {
        add(nodeCount, e);
    } // добавляет элемент в конец

    public Object getFirst() {
        return get(0);
    }

    public Object getLast() {
        return get(nodeCount - 1);
    }

    public Object removeFirst() {
        return remove(0);
    }

    public Object removeLast() {
        return remove(nodeCount - 1);
    }

    @Override

    public void add(Object e) {
        add(nodeCount, e);
    }

    @Override
    public void add(int index, Object e) {
        if (index == 0) {
            Node newNode = new Node(e);
            newNode.next = head;
            head = newNode;
            nodeCount++;
            return;
        }
        Node listNode = getNode(index - 1);
        Node newNode = new Node(e);
        newNode.next = listNode.next;
        listNode.next = newNode;
        nodeCount++;
    }

    @Override
    public void addAll(Object[] c) {
        for (Object element : c) {
            this.add(element);
        }
    }

    @Override
    public void addAll(int index, Object[] c) {
        for (Object element : c) {
            this.add(index, element);
            index++;
        }
    }//добавляет массив элементов в указанное место коллекции, и бросает исключение если индекс выходит за рамки

    private Node getNode(int index) {
        if (index < 0 || index >= nodeCount) {
            throw new IndexOutOfBoundsException();
        }
        Node listNode = head;
        for (int listIndex = 0; listIndex < index; listIndex++) {
            listNode = listNode.next;
        }
        return listNode;
    }

    @Override
    public Object get(int index) {
        return getNode(index).value;
    }//возвращает элемент по индексу, и бросает исключение если индекс выходит за рамки

    @Override
    public Object remove(int index) {
        if (index == nodeCount) {
            throw new IndexOutOfBoundsException();
        }
        if (index == 0) {
            Object res = head.value;
            head = head.next;
            nodeCount--;
            return res;
        }
        Node prev = getNode(index - 1);
        Object res = prev.next.value;
        prev.next = prev.next.next;
        nodeCount--;
        return res;
    }

    @Override
    public void set(int index, Object e) {
        Node listNode = getNode(index);
        listNode.value = e;
    }

    @Override
    public int indexOf(Object o) {
        int index = 0;
        Node currNode = head;
        while (!o.equals(currNode.value) && currNode != null) {
            currNode = currNode.next;
            index++;
        }
        if (currNode.value != o) {
            index = -1;
        }
        return index;
    }//поиск индекса по значению элемента (выводит индекс первого найденного, или -1 в случае его отсутствия)

    @Override
    public int size() {
        return nodeCount;
    }//размер коллекции

    @Override
    public void clear() {
        while (!isEmpty()) {
            remove(0);
        }
    }//удаляет содержимое коллекции

    @Override
    public boolean isEmpty() {
        return nodeCount == 0;
    }//возвращает true если в коллекции нет элементов

    @Override
    public Object[] toArray() {
        Object[] res = new Object[nodeCount];
        int index = 0;
        Node currNode = head;
        while (index < nodeCount) {
            res[index] = currNode.value;
            currNode = currNode.next;
            index++;
        }
        return res;
    } //преобразует коллекцию в массив объектов

    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        int index = 0;
        Node currNode = head;
        while (index < nodeCount) {
            res.append(", ");
            res.append(currNode.value);
            currNode = currNode.next;
            index++;
        }
        if (nodeCount != 0) {
            res.delete(0, 2);
        }
        return res.toString();
    }//возвращает строку, в которой через запятую выводятся значения элементов в коллекции

    private static class Node {

        Object value;
        Node next;

        Node(Object value) {
            this.value = value;
        }
    }

}
